use std::{collections::HashMap, net::SocketAddr};

use futures::{SinkExt, StreamExt, TryStreamExt};

use tokio::{
    net::TcpStream,
    sync::{
        mpsc::{self, UnboundedReceiver, UnboundedSender},
        RwLock,
    },
};
use tokio_tungstenite::{
    accept_hdr_async,
    tungstenite::{
        handshake::server::{Request, Response},
        Message,
    },
};

pub static WEBSOCKETS: std::sync::LazyLock<RwLock<HashMap<String, UnboundedSender<Message>>>> =
    std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));

pub struct Websocket {}

impl Websocket {
    pub async fn accept_connection(stream: TcpStream, addr: SocketAddr) -> anyhow::Result<()> {
        let callback = |req: &Request, response: Response| {
            /* debug!("Received a new ws handshake");
                       debug!("The request's path is: {}", req.uri().path());
                       debug!("The request's headers are:");

                       for (ref header, _value) in req.headers() {
                           debug!("* {}: {:?}", header, _value);
                       }
            */
            /* let headers = response.headers_mut();
            let header_value:HeaderValue = "".parse().unwrap();
            headers.append("MyCustomHeader", header_value); */
            Ok(response)
        };

        let (tx, mut rx): (UnboundedSender<Message>, UnboundedReceiver<Message>) =
            mpsc::unbounded_channel();

        let mut websockets = WEBSOCKETS.write().await;

        websockets.entry(addr.to_string()).or_insert_with(|| tx);
        drop(websockets);

        let ws_stream = accept_hdr_async(stream, callback).await?;
        //let ws_stream = accept_async(stream).await?;
        let (mut socket_writer, mut socket_reader) = ws_stream.split();

        tokio::spawn(async move {
            while let Some(msg) = rx.recv().await {
                match socket_writer.send(msg).await {
                    Ok(()) => {
                        log::debug!("websocket ip -> {:?} send msg  success", &addr);
                    }
                    Err(err) => {
                        tracing::error!("send websocket msg error {}", err);
                        socket_writer.close().await.expect("close socket error");
                        let mut websockets = WEBSOCKETS.write().await;

                        websockets.remove(&addr.to_string());

                        drop(websockets);
                        /* Some(CloseFrame {
                            code: CloseCode::Error,
                            reason: err.to_string().into(),
                        }) */
                    }
                }
            }
        });

        while let Ok(Some(msg)) = socket_reader.try_next().await {
            handle_msg(msg).await;
        }

        Ok(())
    }
}

pub async fn send_msg(msg: &str) -> anyhow::Result<()> {
    Ok(())
}

pub async fn handle_msg(msg: Message) {
    if msg.is_text() || msg.is_binary() {
        log::debug!("websocket server receive message -> {:?}", &msg);
    }
}
